package com.gitee.wsl.io.fetcher

import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.io.bytestring.ByteString


/**
 * Fetchers get a desired [ByteString] if possible.
 */
internal interface Fetcher<out T : ByteString?> {
    /**
     * Returns the desired [ByteString], or null if this fetcher doesn't know how to fetch this
     * resource.
     *
     * @throws Exception if this fetcher knows how to fetch this resource but was unsuccessful.
     */
    suspend fun fetch(
        applicationName: String,
        eventListener: EventListener,
        id: String,
        sha256: ByteString,
        nowEpochMs: Long,
        baseUrl: String?,
        url: String,
    ): T
}

/**
 * Use a [concurrentDownloadsSemaphore] to control parallelism of fetching operations.
 */
internal suspend fun List<Fetcher<ByteString?>>.fetch(
    concurrentDownloadsSemaphore: Semaphore,
    applicationName: String,
    eventListener: EventListener,
    id: String,
    sha256: ByteString,
    nowEpochMs: Long,
    baseUrl: String?,
    url: String,
): ByteString? = concurrentDownloadsSemaphore.withPermit {
    var firstException: Exception? = null
    for (fetcher in this) {
        try {
            return@withPermit fetcher.fetch(
                applicationName = applicationName,
                eventListener = eventListener,
                id = id,
                sha256 = sha256,
                nowEpochMs = nowEpochMs,
                baseUrl = baseUrl,
                url = url,
            ) ?: continue
        } catch (e: Exception) {
            if (firstException == null) {
                firstException = e
            }
        }
    }
    if (firstException != null) throw firstException
    return@withPermit null
}

interface EventListener{
     fun cacheHit(applicationName: String, url: String, toLong: Long)

}